/* Copyright (c) 2009-2010, Sean A.O. Harney <sharney3@gmail.com> */

#include "omf.h"


int write_fd;

bool socket_launch(const char *hostname,
		   unsigned int port, int type, const char *vhost)
{
    int pipe_fds[2];
    pid_t pid;

    if (pipe(pipe_fds) == -1)
      {
	  perror("pipe()");
	  return false;
      }

    pid = fork();
    switch (pid)
      {
      case -1:
	  perror("fork()");
	  close(pipe_fds[0]);
	  close(pipe_fds[1]);
	  return false;
      case 0:			/* child process (pipe reader) */
	  debug1_printf("socket_launch child process: %d\n", getpid());
	  close(pipe_fds[1]);	// close write pipe
	  int sock;
#define MAX_LEN 32000
	  uint8_t buf[MAX_LEN];
	  size_t len, total, r, needed;
	  while (1)
	    {
		sock = socket_connect(hostname, port, type, vhost);
		if (sock == -1)
		    break;
		r = recv(pipe_fds[0], &len, sizeof(size_t), 0);
		if (r < 0)
		    fatal_error_perror("recv()");
//assert len <=MAX_LEN
//TODO: maybe recv even chunks instead
		for (total = 0; total != len; total += r)
		  {
		      needed = len - total;
		      r = recv(pipe_fds[0], buf, len - total, 0);
		      if (r < 0)
			  fatal_error_perror("recv()");

		      if (!socket_send(sock, buf, len))
			  fatal_error_println("socket_send()");

		  }

	    }

	  return false;
      default:			/* parent process (pipe writer) */
	  // = pid;     // set global
	  usleep(1000);		// give the exec some time, TODO: better way of doing this?
	  close(pipe_fds[0]);	// close read pipe
	  write_fd = pipe_fds[1];
	  return true;
      }
}



// vhost = NULL for no bind()
int socket_connect(const char *hostname, unsigned int port, int type,
		   const char *vhost)
{
    int sock;
    struct hostent *he, *hel;
    struct sockaddr_in saddr;
    struct sockaddr_in localaddr;

    if (type != SOCK_STREAM && type != SOCK_DGRAM)
      {
	  fprintf(stderr, "invalid socket type.\n");
	  return -1;
      }

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
      {
	  perror("socket()");
	  return -1;
      }


    if (vhost)
      {
	  if ((hel = gethostbyname(vhost)) == NULL)
	    {
		herror("gethostbyname()");
		close(sock);
		return -1;
	    }

	  memset(&localaddr, 0, sizeof(struct sockaddr_in));

	  localaddr.sin_family = AF_INET;
	  localaddr.sin_port = 0;
	  localaddr.sin_addr = *((struct in_addr *) hel->h_addr);

/* this is to use VHOST */

	  if (bind
	      (sock, (struct sockaddr *) &localaddr, sizeof(localaddr)))
	    {
		perror("bind()");
		close(sock);
		return -1;
	    }
      }


    if ((he = gethostbyname(hostname)) == NULL)
      {
	  herror("gethostbyname()");
	  close(sock);
	  return -1;
      }


    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr = *((struct in_addr *) he->h_addr);
    if (connect
	(sock, (struct sockaddr *) &saddr, sizeof(struct sockaddr)) == -1)
      {
	  perror("connect()");
	  close(sock);
	  return -1;
      }
    return sock;
}

void socket_close(int sock)
{
    if (close(sock) < 0)
	perror("socket_close()");

}
bool socket_send(int sock, uint8_t * buf, size_t len)
{
    size_t r, total;

    for (total = 0; total != len; total += r)
      {
	  r = send(sock, buf, total - len, 0);
	  if (r < 0)
	    {
		perror("send()");
		return false;
	    }
	  if (r != len)
	      fprintf(stderr, "socket_send(): r != len\n");
	  return false;
      }
    return true;
}
